﻿namespace KinonekoSoftware.CodariaDB.Runtimes
{
    /// <summary>
    /// 
    /// </summary>
    /// <remarks>
    /// <para>
    /// <see cref="ValuePool"/> 是一个非常操作非常耗时的类，我们保守估计这个类型的性能为 4000 op/s
    /// </para>
    /// <para>
    /// <see cref="ValuePool"/> 的性能会比原生的<see cref="Dictionary{TKey,TValue}"/>慢至少1000倍，主要的原因是由于 <see cref="ILiteCollection{T}"/> 的性能局限所导致的，自身抽象仅比原生的<see cref="Dictionary{TKey,TValue}"/>慢2~4倍。
    /// </para>
    /// </remarks>
    public abstract class ValuePool : ObjectPool
    {

        #region IValuePool Impls
        
        private sealed class StringValuePool : ValuePoolImpl<string>
        {
            public StringValuePool(ILiteDatabase database, string name, Action changeCallback) : base(database, name, changeCallback)
            {
            }

            protected override string Get(BsonValue value) => value.AsString;
        }
        
        private sealed class BooleanValuePool : ValuePoolImpl<bool>
        {
            public BooleanValuePool(ILiteDatabase database, string name, Action changeCallback) : base(database, name, changeCallback)
            {
            }

            protected override bool Get(BsonValue value) => value.AsBoolean;
        }
        
        private sealed class IntegerValuePool : ValuePoolImpl<int>
        {
            public IntegerValuePool(ILiteDatabase database, string name, Action changeCallback) : base(database, name, changeCallback)
            {
            }

            protected override int Get(BsonValue value) => value.AsInt32;
        }

        private abstract class ValuePoolImpl<TValue> : IValuePool<TValue>
        {
            private const string ID_Value = "v";
            private const string ID_ID    = "_id";

            private readonly ILiteCollection<BsonDocument> _objects;
            private readonly Action                        _changeCallback;

            protected ValuePoolImpl(ILiteDatabase database, string name, Action changeCallback)
            {
                _objects        = database.GetCollection<BsonDocument>(name);
                _changeCallback = changeCallback ?? throw new ArgumentNullException(nameof(changeCallback));
            }

            protected virtual TValue Get(BsonValue value) => default(TValue);

            public bool Has(string key) => !string.IsNullOrEmpty(key) && _objects.HasID(key);

            public TValue Remove(string key)
            {
                if (string.IsNullOrEmpty(key))
                {
                    return default(TValue);
                }

                var obj = _objects.FindById(key);


                if (obj is null)
                {
                    return default(TValue);
                }

                var doc   = obj.AsDocument;
                var value = doc[ID_Value];
                return DatabaseBsonMapper.Override.Deserialize<TValue>(value);
            }

            public TValue this[string key]
            {
                get
                {
                    if (string.IsNullOrEmpty(key))
                    {
                        return default(TValue);
                    }

                    var vo = _objects.FindById(key);

                    if (vo is null)
                    {
                        return default(TValue);
                    }

                    var doc   = vo.AsDocument;
                    var value = doc[ID_Value];
                    
                    return Get(value);
                }

                set
                {
                    if (string.IsNullOrEmpty(key))
                    {
                        return;
                    }

                    if (_objects.Upsert(new BsonDocument
                        {
                            { ID_ID, key },
                            { ID_Value, new BsonValue(value) },
                        }))
                    {
                        _changeCallback?.Invoke();
                    }
                }
            }
        }

        #endregion

        private readonly ValuePoolImpl<int>    _int;
        private readonly ValuePoolImpl<bool>   _bool;
        private readonly ValuePoolImpl<string> _string;


        protected ValuePool(LiteDatabase database) : base(database)
        {
            _int    = new IntegerValuePool(database, CollectionNames.IntegerDB, Modified);
            _bool   = new BooleanValuePool(database, CollectionNames.BooleanDB, Modified);
            _string = new StringValuePool(database, CollectionNames.StringDB, Modified);
        }

        protected void Modified()
        {
            Version++;
        }

        /// <summary>
        /// 
        /// </summary>
        public IValuePool<string> Strings => _string;


        /// <summary>
        /// 
        /// </summary>
        public IValuePool<int> Integers => _int;


        /// <summary>
        /// 
        /// </summary>
        public IValuePool<bool> Booleans => _bool;


        public int Version { get; private set; }
    }
}